Cada ventana tiene una función asociada, esta función se conoce como procedimiento de ventana, y es la encargada de procesar adecuadamente todos los mensajes enviados a una determinada clase de ventana. Es la responsable de todo lo relativo al aspecto y al comportamiento de una ventana.
Normalmente, estas funciones están basadas en una estructura “switch” donde cada “case” corresponde aun determinado tipo de mensaje.
LRESULT CALLBACK WindowProcedure ( HWND hwnd, // Manipulador de ventana destinado del mensaje UINT msg, // Código id del mensaje WPARAM wParam, // Parámetro del mensaje tipo palabra LPARAM lParam // Parámetro del mensaje tipo doble );
LRESULT CALLBACK WindowProcedure
(
HWND hwnd, // Manipulador de ventana destinado del mensaje
UINT msg, // Código id del mensaje
WPARAM wParam, // Parámetro del mensaje tipo palabra
LPARAM lParam // Parámetro del mensaje tipo doble
);Todas las funciones de ventana han de tener los mismos valor de retorno y parámetros.
El miembro lpfnWndProc de la estructura WNDCLASS es un puntero a una función de este tipo, esa función es la que se encargará de procesar todos los mensajes para esa clase de ventana. Cuando registremos nuestra clase de ventana, tendremos que asignar a ese miembro el puntero a nuestro procedimiento de ventana.
Para más detalles sobre la función de procedimiento de ventana, consultar WindowProc.
/* Esta función es llamada por la función del API DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) // manipulador del mensaje
{
case WM_DESTROY:
PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes
break;
default: // para los mensajes de los que no nos ocupamos
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}/* Esta función es llamada por la función del API DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) // manipulador del mensaje
{
case WM_DESTROY:
PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes
break;
default: // para los mensajes de los que no nos ocupamos
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}En general, habrá tantos procedimientos de ventana como programas diferentes y todos serán distintos, pero también tendrán algo en común: todos ellos procesarán los mensajes que lleguen a una clase de ventana.
En este ejemplo sólo procesamos un tipo de mensaje, se trata de WM_DESTROY que es el mensaje que se envía a una ventana cuando se recibe un comando de cerrar, ya sea por menú o mediante el icono del aspa en la esquina superior derecha de la ventana.
Este mensaje sólo indica que el usuario tiene la intención de abandonar la aplicación, para cerrar ficheros, liberar memoria, guardar variables, etc, o incluso para cancelar la salida de la aplicación.
En el caso del ejemplo, efectivamente cierra la aplicación, y lo hace enviándole un mensaje WM_QUIT, mediante la función PostQuitMessage.
El resto de los mensajes se procesan en el caso “default”, y simplemente se cede su tratamiento a la función del API que hace el proceso por defecto para cada mensaje, DefWindowProc. Este es el camino que sigue el mensaje WM_QUIT cuando llega, ya que el proceso por defecto para este mensaje es cerrar la aplicación.
En posteriores capítulos veremos como se complica paulatinamente esta función, añadiendo más y más mensajes.
#include <windows.h>
// http://winapi.conclase.net/curso/index.php?cap=004#inicio
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); // Prototipo del procedimiento de ventana
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) // función principal WinMain
{
HWND hwnd; // Manipulador de ventana
MSG mensaje; // Mensajes recibidos por la aplicación
WNDCLASSEX wincl; // Estructura de datos para la clase de ventana
/* Estructura de ventana */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = "NUESTRA_CLASE";
wincl.lpfnWndProc = WindowProcedure; // Función invocada por Windows
wincl.style = CS_DBLCLKS; // Captura los doble-clicks
wincl.cbSize = sizeof (WNDCLASSEX);
/* Usar icono y puntero por defecto */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; // Sin menú
wincl.cbClsExtra = 0; // Sin información adicional para la clase
wincl.cbWndExtra = 0; // Sin información adicional para la ventana
wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND); // Color de fondo por defecto para la ventana
/* Registrar la clase de ventana */
if (!RegisterClassEx(&wincl))
{
return 0; // si falla, sale del programa
}
/* Crear la ventana */
hwnd = CreateWindowEx(0, // se puede moficiar
"NUESTRA_CLASE", // Nombre de la clase
"Ejemplo 001", // Texto del título
WS_OVERLAPPEDWINDOW, // Tipo por defecto
CW_USEDEFAULT, // Posición X de la ventana, por defecto
CW_USEDEFAULT, // Posición Y de la ventana, por defecto
544, // Ancho
375, // Alto en pixels
HWND_DESKTOP, // La ventana es hija del escritorio
NULL, // Sin menú
hThisInstance, // Manipulador de instancia
NULL ); // No hay datos de creación de ventana
ShowWindow(hwnd, SW_SHOWDEFAULT); // Mostrar la ventana
/* Bucle de mensajes, se ejecuta hasta que haya error o GetMessage devuelva FALSE */
while(TRUE == GetMessage(&mensaje, NULL, 0, 0))
{
TranslateMessage(&mensaje); // Traducir mensajes de teclas virtuales a mensajes de caracteres
DispatchMessage(&mensaje); // Enviar mensaje al procedimiento de ventana
}
return mensaje.wParam; // Salir con valor de retorno
}
/* Esta función es invocada por la función DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
switch (mensaje) /* manipulador de mensaje */
{
case WM_DESTROY:
PostQuitMessage (0); /* Envía el mensaje WM_QUIT a la cola de mensajes */
break;
default: /* Mensajes que no queremos manejar */
return DefWindowProc (hwnd, mensaje, wParam, lParam);
}
return 0;
}#include <windows.h>
// http://winapi.conclase.net/curso/index.php?cap=004#inicio
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); // Prototipo del procedimiento de ventana
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) // función principal WinMain
{
HWND hwnd; // Manipulador de ventana
MSG mensaje; // Mensajes recibidos por la aplicación
WNDCLASSEX wincl; // Estructura de datos para la clase de ventana
/* Estructura de ventana */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = "NUESTRA_CLASE";
wincl.lpfnWndProc = WindowProcedure; // Función invocada por Windows
wincl.style = CS_DBLCLKS; // Captura los doble-clicks
wincl.cbSize = sizeof (WNDCLASSEX);
/* Usar icono y puntero por defecto */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; // Sin menú
wincl.cbClsExtra = 0; // Sin información adicional para la clase
wincl.cbWndExtra = 0; // Sin información adicional para la ventana
wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND); // Color de fondo por defecto para la ventana
/* Registrar la clase de ventana */
if (!RegisterClassEx(&wincl))
{
return 0; // si falla, sale del programa
}
/* Crear la ventana */
hwnd = CreateWindowEx(0, // se puede moficiar
"NUESTRA_CLASE", // Nombre de la clase
"Ejemplo 001", // Texto del título
WS_OVERLAPPEDWINDOW, // Tipo por defecto
CW_USEDEFAULT, // Posición X de la ventana, por defecto
CW_USEDEFAULT, // Posición Y de la ventana, por defecto
544, // Ancho
375, // Alto en pixels
HWND_DESKTOP, // La ventana es hija del escritorio
NULL, // Sin menú
hThisInstance, // Manipulador de instancia
NULL ); // No hay datos de creación de ventana
ShowWindow(hwnd, SW_SHOWDEFAULT); // Mostrar la ventana
/* Bucle de mensajes, se ejecuta hasta que haya error o GetMessage devuelva FALSE */
while(TRUE == GetMessage(&mensaje, NULL, 0, 0))
{
TranslateMessage(&mensaje); // Traducir mensajes de teclas virtuales a mensajes de caracteres
DispatchMessage(&mensaje); // Enviar mensaje al procedimiento de ventana
}
return mensaje.wParam; // Salir con valor de retorno
}
/* Esta función es invocada por la función DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
switch (mensaje) /* manipulador de mensaje */
{
case WM_DESTROY:
PostQuitMessage (0); /* Envía el mensaje WM_QUIT a la cola de mensajes */
break;
default: /* Mensajes que no queremos manejar */
return DefWindowProc (hwnd, mensaje, wParam, lParam);
}
return 0;
}